package edu.internet2.middleware.assurance.mcb.sub.safenet;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import securecomputing.ssl.SimpleSSLClient;
import securecomputing.swec.AuthenState;
import securecomputing.swec.AuthenticatorData;
import securecomputing.swec.DynamicPwdData;
import securecomputing.swec.EasspMessage;
import securecomputing.swec.FixedPwdData;
import securecomputing.swec.SafeWordClient;
import securecomputing.swec.SafeWordClientPool;
import securecomputing.swec.SwecCode;
import securecomputing.swec.SwecConfig;

public class AuthenticationEngine {

	private final Logger log = LoggerFactory.getLogger(AuthenticationEngine.class);
	private SafeWordClientPool swPool = null;
	
	/**
	 * Create an instance of the SafeNet Authentication engine including a pool of clients to use.
	 * 
	 * @param safenetServerName The SafeNet/Safeword 2008 server hostname or IP address.
	 * @param safenetServerPort The SafeNet/Safeword 2008 server port.
	 */
	public AuthenticationEngine(String safenetServerName, String safenetServerPort) {
		SimpleSSLClient.seedRandomGenerator();
		SwecConfig config = new SwecConfig();
		
		try {
			config.setDefaults();
			String tmp = safenetServerName + " " + safenetServerPort;
			config.setProperty(SwecConfig.SERVER_SPEC, tmp);
			config.setProperty(SwecConfig.EASSP_VERSION, "202");
			config.setProperty(SwecConfig.AGENT_NAME, "SafeNetLoginSubmodule");
			config.setProperty(SwecConfig.CONNECTION_POOL_SIZE, "50");
			config.setProperty(SwecConfig.CONNECTION_POOL_PERSISTENCE_SEC, "900");
			swPool = new SafeWordClientPool(config);
		} catch (securecomputing.swec.AuthenticationException ex) {
			log.error("Error creating Safeword Pool.", ex);
			throw ex;
		}
		return;
	}
	
	/**
	 * Try to authenticate a user against the SafeNet/Safeword instance.
	 * 
	 * @param loginId The user's loginID as given.
	 * @param password The user's password as given. Should include PIN if PINs are used.
	 * 
	 * @return Results of the login attempt.
	 */
    public AuthResult loginUser(String loginId, String password) {
    	EasspMessage responseMsg, requestMsg, resultMsg, challengeMsg = null;
    	SafeWordClient swClient = null;
    	boolean bContinue;
    	AuthResult result = new AuthResult();

    	try {
    		// get a SafeWordClient object from the pool
    		swClient = swPool.getClient();
    	} catch (Exception ex) {
    		result.setResultMessage("Exception while getting auth client from pool.");
    		result.setResultStatus(false);
    		result.setResultCode(99);
    		log.error("Exception getting auth client from pool", ex);
    		return result;
    	}

    	try {
    		bContinue = true; // assume ok to start
    		// Create the request message containing the username
    		requestMsg = swClient.createRequestMsg(loginId, "name");
    		// Send the initial request to the AAA server
    		resultMsg = swClient.sendMessage(requestMsg);

    		// determine the message type recieved
    		switch (resultMsg.getMessageType()) {
	    		case EasspMessage.AUTHENTICATION_CHALLENGE:
	    			log.debug("Receiving authentication challenge response from Safeword for request.");
	    			challengeMsg = resultMsg;
	    			break;
	    		case EasspMessage.AUTHENTICATION_RESULT:
	    			// got a result so tell what it is and exit
	    			swClient.close();
	    			if (resultMsg.getStatusText() != null) {
	    				result.setResultMessage(resultMsg.getStatusText());
	    				result.setResultCode(resultMsg.getReturnCode());
	    			} else {
	    				switch (resultMsg.getReturnCode()) {
	    				case SwecCode.SERVER_FAIL:
	    					result.setResultMessage("Server Failure");
	    					break;
	    				default:
	    					result.setResultMessage("Unknown Failure");
	    					break;
	    				}
	    			}
	    			result.setResultCode(resultMsg.getReturnCode());
	    			bContinue = resultMsg.passedCheck();
	    			break;
	    		default:
	    			result.setResultMessage(resultMsg.getStatusText());
	    			result.setResultCode(resultMsg.getReturnCode());
	    			bContinue = false;
	    			break;
	    		}

    		// Failing here likely means the loginId is invalid or inactive.
    		if (bContinue == false) {
    			swPool.returnClient(swClient);
    			log.debug("Error occurred while sending inital request. [{}]", result.getResultMessage());
    			return result;
    		}

    		// Now we must take the challenge message and use it to send the password
    		FixedPwdData fp;
    		DynamicPwdData dp;
    		AuthenState state = new AuthenState(challengeMsg);

    		// Get the authenticator data and cast to the appropriate type
    		AuthenticatorData data = state.getCurrentAuthenticator();
    		if (data instanceof FixedPwdData) {
    			log.trace("Found FixedPwdData object. Setting password.");
    			fp = (FixedPwdData)data;
    			fp.setPwd(password);
    		} else if (data instanceof DynamicPwdData) {
    			log.trace("Found DynamicPwdData object. Setting password.");
    			dp = (DynamicPwdData)data;
    			dp.setPwd(password);
    		}

    		// create an Authentication Response message
    		responseMsg = swClient.createResponseMsg(state);
    		// send the response message
    		resultMsg = swClient.sendMessage(responseMsg);
    		if (resultMsg.passedCheck() == true) {
    			result.setResultCode(0);
    			result.setResultStatus(true);
    			result.setResultMessage("No Error");
        		swPool.returnClient(swClient);
    			return result;
    		}
    	} catch (Exception ee) {
    		log.error("Exception talking to Safeword auth server.", ee);
    		result.setResultCode(99);
    		result.setResultMessage("Exception talking to Safeword auth server. " + ee.getMessage());
    		result.setResultStatus(false);
    		swPool.returnClient(swClient);
    		return result;
    	}

    	swPool.returnClient(swClient);
    	// something failed, return the failure
    	result.setResultMessage(resultMsg.getStatusText());
    	result.setResultCode(resultMsg.getReturnCode());
    	result.setResultStatus(false);

    	log.debug("Authentication failed with code [{}] and message [{}]", result.getResultCode(), result.getResultMessage());

    	return result;
    }

}
